﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.ServiceModel;
using PSE.Framework.Common.Security.Services.Authentication.ClientCredentials;
using System.IdentityModel.Claims;
using PSE.Framework.Service.Security.Identity;

namespace PSE.Framework.Service.Security.Authentication
{
    /// <summary>
    /// Classe responsavel por expor informações do contexto de authenticacao
    /// </summary>
    public sealed class AuthenticationContext
    {
        #region Constructor
        private AuthenticationContext()
        {
        }
        #endregion

        #region Instance Properties

        /// <summary>
        /// Dicionário onde serão armazenados os dados do contexto de autenticacao
        /// </summary>
        private Dictionary<string, object> ContextData
        {
            get;
            set;
        }

        public ClientHost ClientHost
        {
            get;
            set;
        }

        public CustomClientCredentials ClientCredentials
        {
            get;
            internal set;
        }
        #endregion


        #region Static Properties
        private static Dictionary<int, AuthenticationContext> _instances = new Dictionary<int, AuthenticationContext>();
        public static AuthenticationContext Current
        {
            get
            {
                lock (_instances)
                {
                    if (_instances.ContainsKey(System.Threading.Thread.CurrentThread.ManagedThreadId))
                        return _instances[System.Threading.Thread.CurrentThread.ManagedThreadId];
                    else return null;
                }

            }
        }
        #endregion

        #region Instance Methods
        /// <summary>
        /// Criar as credenciais
        /// </summary>
        protected void CreateClientCredentials()
        {
            //Criar dicionário para armazenar dados do contexto
            this.ContextData = new Dictionary<string, object>();

            //TODO: Achar uma forma de criar uma client credential de acordo com o tipo de autenticacao utilizado
            this.ClientCredentials = new CustomClientCredentials();

            System.IdentityModel.Policy.AuthorizationContext athzContext = OperationContext.Current.ServiceSecurityContext.AuthorizationContext;

            //Varre os claimSets do contexto de Operação.
            foreach (ClaimSet cs in athzContext.ClaimSets)
            {
                //Varre as Claims de Autorização do Usuário
                foreach (Claim c in cs.FindClaims(BaseClaimTypes.Identity.LoginName, Rights.PossessProperty))
                    this.ClientCredentials.UserName.UserName = c.Resource.ToString();

                foreach (Claim c in cs.FindClaims(BaseClaimTypes.Identity.Password, Rights.PossessProperty))
                    this.ClientCredentials.UserName.Password = c.Resource.ToString();
            }
        }

        /// <summary>
        /// Armazenar dados no contexto de autenticação
        /// </summary>
        /// <param name="key"></param>
        /// <param name="value"></param>
        public void StoreData(string key, object value)
        {
            lock (this.ContextData)
            {
                if (!this.ContextData.ContainsKey(key))
                    this.ContextData.Add(key, value);
                else
                    this.ContextData[key] = value;
            }
        }

        /// <summary>
        /// Remover dados armazenados no contexto de autenticação
        /// </summary>
        /// <param name="key"></param>
        public void RemoveData(string key)
        {
            lock (this.ContextData)
            {
                if (this.ContextData.ContainsKey(key))
                    this.ContextData.Remove(key);
            }
        }

        /// <summary>
        /// Recuperar dados armazenados no contexto de autenticação
        /// </summary>
        /// <param name="key"></param>
        /// <returns></returns>
        public object GetData(string key)
        {
            lock (this.ContextData)
            {
                if (this.ContextData.ContainsKey(key))
                    return this.ContextData[key];
            }

            return null;
        }

        /// <summary>
        /// Verficar se existe dados amarzenados para a key informada
        /// </summary>
        /// <param name="key"></param>
        /// <returns></returns>
        public bool ContainsData(string key)
        {
            return this.ContextData.ContainsKey(key);
        }
        #endregion

        #region Static Methods
        public static void CreateAuthenticationContext()
        {
            AuthenticationContext context = new AuthenticationContext();
            context.CreateClientCredentials();

            lock (_instances)
            {
                if (!_instances.ContainsKey(System.Threading.Thread.CurrentThread.ManagedThreadId))
                {
                    _instances.Add(System.Threading.Thread.CurrentThread.ManagedThreadId, context);
                }
            }
        }

        public static void RemoveAuthenticationContext()
        {
            lock (_instances)
            {
                if (_instances.ContainsKey(System.Threading.Thread.CurrentThread.ManagedThreadId))
                    _instances.Remove(System.Threading.Thread.CurrentThread.ManagedThreadId);
            }
        }
        #endregion
    }
}
